﻿#include "pch.h"
#if _QG_USE_D11
#include "qgdrv_d11_rdh.h"
#include "qgdrv_d11_cnv.h"

//
bool D11Rdh::_CommitShader()
{
	D11Shader* shd = _cache.shd_ptr;

	if (shd == NULL)
	{
		// 하... 어떻게 그리라고
		kobj_tracet(KLV_ERROR, KERR_NOREF, "no shader to draw");

		return false;
	}
	else
	{
		if (!shd->_linked)
		{
			// 링크 안됐음
			kobj_tracet(KLV_WARNING, KERR_NOREF, "no linked shader specified. will be linking instead.");
			kobj_tracef(KLV_WARNING, "target shader is '%s'", shd->_key.Data());

			if (!shd->Link())
				return false;
		}

		// 매핑
		shd->_ProcessMap(_context);

		return true;
	}
}

//
qgShader* D11Rdh::CreateShader(const char* name)
{
	RDHNFO_INVOKE();

	D11Shader* base;
	D11Shader::Key key;

	if (name != NULL)
	{
		key = name;
		base = (D11Shader*)_keep.shaders.Get(&key);
	}
	else
	{
		key.Format("d11_shader_%Lu", k_nth());
		base = NULL;
	}

	if (base != NULL)
		base->Load();
	else
	{
		base = kobj_create(D11Shader);
		base->_key = key;
		_keep.shaders.Add(base);
	}

	return base;
}


//////////////////////////////////////////////////////////////////////////
// 세이더

// include 구문 처리용, 디바이스 마운트에서 파일 가져오기
class D11Shader::InclImpl : public ID3DInclude
{
private:
	qgStub* _stub;

public:
	InclImpl(qgStub* stub)
		: _stub(stub)
	{
	}

	~InclImpl()
	{
	}

	K_OVR HRESULT _stdcall Open(D3D_INCLUDE_TYPE IncludeType, LPCSTR pFileName, LPCVOID pParentData, LPCVOID *ppData, UINT *pBytes)
	{
		*ppData = _stub->FileRead(pFileName, (kint*)pBytes);

		return S_OK;
	}

	K_OVR HRESULT _stdcall Close(LPCVOID pData)
	{
		k_delete(pData);

		return S_OK;
	}
};

//
void D11Shader::TypeInitialize(kType type)
{
	kTsb t(type);
	t.SetFuncKey(&this_type::BaseKey);
	t.SetFuncEq((khEqFunc)&this_type::BaseEq);
	t.SetFuncHash((khHashFunc)&this_type::KeyHash);

	_AutoInitFunc();
}

//
D11Shader::~D11Shader() throw()
{
	if (_node_desc)
	{
		ID3D11InputLayout* il = (ID3D11InputLayout*)_node_desc;
		il->Release();
	}

	_RemoveData(_ps, _bd_ps, false);
	_RemoveData(_vs, _bd_vs, false);

	((D11Rdh*)qgRdh::Instance)->_RoamShaderUnload(this);
}

//
void D11Shader::_OutputError(ID3DBlob* errs, kint errcode, const char* target)
{
	kobj_tracef(KLV_ERROR, "%s with shader '%s'", k_errmsg(errcode), target);

	if (KLV_ERROR >= k_getlv() && errs)
	{
		if (errs->GetBufferSize() > 0)
		{
			const char* psz = (const char*)errs->GetBufferPointer();
			k_mesgs(psz);
		}
	}
}

//
bool D11Shader::_CreatePixelShader(kconstpointer data, kint size, D3D_SHADER_MACRO* mcs, InclImpl& inc, DWORD dw, const char* signature) throw()
{
	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;
	const char* target = rdh->GetProfilePS();

	ID3DBlob* blob = NULL;
	ID3DBlob* errs = NULL;
	HRESULT hr;
	kint ret;

	_RemoveData(_ps, _bd_ps, true);

	//
	hr = D11Rdh::D3DCOMPILE(data, size, signature, mcs, &inc, "PS",
		target, dw, 0, &blob, &errs);

	if (FAILED(hr))
		ret = KERR_PARSE;
	else
	{
		ksize_t blobsize = blob->GetBufferSize();
		kpointer blobdata = blob->GetBufferPointer();

		hr = rdh->GetDevice()->CreatePixelShader(blobdata, blobsize, NULL, &_ps);

		if (FAILED(hr))
			ret = KERR_FAIL_CREATE;
		else
		{
			// 성공
			ret = KERR_SUCCESS;
			_bd_ps = _AnalyzeData(QGSHADER_PS, blobdata, blobsize, false, false);
		}
	}

	//
	if (ret == KERR_SUCCESS)
	{
		kunk_release(blob);
		return true;
	}
	else
	{
		_OutputError(errs, KERR_PARSE, target);
		kunk_release(blob);
		kunk_release(errs);
		return false;
	}
}

//
bool D11Shader::_CreateVertexShader(kconstpointer data, kint size, D3D_SHADER_MACRO* mcs, InclImpl& inc, DWORD dw, const char* signature) throw()
{
	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;
	const char* target = rdh->GetProfileVS();

	ID3DBlob* blob = NULL;
	ID3DBlob* errs = NULL;
	HRESULT hr;
	kint ret;

	_RemoveData(_vs, _bd_vs, true);

	//
	hr = D11Rdh::D3DCOMPILE(data, size, signature, mcs, &inc, "VS",
		target, dw, 0, &blob, &errs);

	if (FAILED(hr))
		ret = KERR_PARSE;
	else
	{
		ksize_t blobsize = blob->GetBufferSize();
		kpointer blobdata = blob->GetBufferPointer();

		hr = rdh->GetDevice()->CreateVertexShader(blobdata, blobsize, NULL, &_vs);

		if (FAILED(hr))
			ret = KERR_FAIL_CREATE;
		else
		{
			// 성공
			ret = KERR_SUCCESS;
			_bd_vs = _AnalyzeData(QGSHADER_VS, blobdata, blobsize, true, true);
		}
	}

	//
	if (ret == KERR_SUCCESS)
	{
		kunk_release(blob);
		return true;
	}
	else
	{
		_OutputError(errs, KERR_PARSE, target);
		kunk_release(blob);
		kunk_release(errs);
		return false;
	}
}

//
D3D_SHADER_MACRO* D11Shader::_MakeDefinition(const DefArray& defs)
{
	if (defs.empty())
		return NULL;

	D3D_SHADER_MACRO* mcs = k_new(defs.size() + 1, D3D_SHADER_MACRO);
	D3D_SHADER_MACRO* p = mcs;

	kForEach(auto& str, defs)
	{
		p->Name = str.c_str();
		p->Definition = "1";	// 문자열은 상수니깐 설정해도 괜찮다.
		p++;
	}

	// 마지막은 NULL로 끝나지 않으면 안된다고 하더군
	p->Name = NULL;
	p->Definition = NULL;

	return mcs;
}

//
DWORD D11Shader::_MakeFlags(kint v)
{
	DWORD dw = D3DCOMPILE_PACK_MATRIX_ROW_MAJOR;

#if _SB_DEBUG_
	dw |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION;
#else
	dw |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
#endif

	// DX9 때부터 써오던 플래그... 인데 필요함?
#if 0
	if (v & 0x0001)
		dw |= D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;

	if (v & 0x0002)
		dw |= D3DCOMPILE_PACK_MATRIX_ROW_MAJOR;
#endif

	if (v & 0x0004)
		dw |= D3DCOMPILE_PARTIAL_PRECISION;

	return dw;
}

//
template <typename T>
void D11Shader::_RemoveData(tpl::RefPtr<T>& ifp, BufferData*& bd, bool reset)
{
	ifp = NULL;

	//
	if (!bd || k_atomdec(&bd->ref) != 0)
	{
		if (reset)
			bd = NULL;
	}
	else
	{
		if (!bd->maps.empty())
		{
			kForEach(MapBuffer* m, bd->maps)
				delete m;
		}

		delete bd;

		if (reset)
			bd = NULL;
	}
}

//
D11Shader::BufferData* D11Shader::_AnalyzeData(qgShaderType type, kpointer data, ksize_t size, bool keep, bool parseinput)
{
	BufferData* bd;
	D11Rdh* rdh = (D11Rdh*)qgRdh::Instance;
	HRESULT hr;
	UINT i, n;

	bd = new BufferData();
	bd->ref = 1;

	// 보관
	if (keep)
	{
		bd->keep.resize(size);

		for (ksize_t z = 0; z < size; z++)
			bd->keep[z] = ((kbyte*)data)[z];
	}

	// 리플렉션...이라는군
	D3D11_SHADER_DESC desc;

#if _MSC_VER && _MSC_VER < 1800
	static const GUID GUID_ID3D11ShaderReflection = {0x8d536ca1, 0x0cca, 0x4956, {0xa8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84}};

	if (FAILED(D11Rdh::D3DREFLECT(data, size, GUID_ID3D11ShaderReflection, (void**)bd->sr.InitPtr())) ||
		FAILED(bd->sr->GetDesc(&desc)))
#else
	if (FAILED(D11Rdh::D3DREFLECT(data, size, __uuidof(ID3D11ShaderReflection), (void**)bd->sr.InitPtr())) ||
		FAILED(bd->sr->GetDesc(&desc)))
#endif
	{
		delete bd;
		return NULL;
	}

	// 입력 파라미터 조사... 인데 필요할까...
	if (parseinput)
	{
		//-- 해야지...
		for (i = 0; i < desc.InputParameters; i++)
		{
			D3D11_SIGNATURE_PARAMETER_DESC prm;
			bd->sr->GetInputParameterDesc(i, &prm);
			//k_mesgf("* %d -> %s\n", i, prm.SemanticName);
		}
	}

	// MapBuffer.buf 버퍼 만들 준비 사전 작업
	D3D11_BUFFER_DESC cbd;
	cbd.Usage = D3D11_USAGE_DYNAMIC;
	cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	cbd.MiscFlags = 0;
	cbd.StructureByteStride = 0;

	// 상수 버퍼 만들고, 상수 매퍼 만들기
	bd->maps.resize(desc.ConstantBuffers);

	for (n = 0; n < desc.ConstantBuffers; n++)
	{
		ID3D11ShaderReflectionConstantBuffer* cb = bd->sr->GetConstantBufferByIndex(n);

		// 버퍼 맵 만들기
		MapBuffer* pm = new MapBuffer();

		//
		cb->GetDesc(&pm->desc);

		cbd.ByteWidth = pm->desc.Size;
		hr = rdh->GetDevice()->CreateBuffer(&cbd, NULL, pm->buf.InitPtr());

		if (FAILED(hr))
		{
			// 헐 버퍼 만들기 실패
			delete pm;

			kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
			continue;
		}

		//
		UINT cvar = 0, nvar = pm->desc.Variables;
		qbShaderVar* vars = k_new(nvar, qbShaderVar);

		for (i = 0; i < nvar; i++)
		{
			ID3D11ShaderReflectionVariable* rv = cb->GetVariableByIndex(i);
			D3D11_SHADER_VARIABLE_DESC svd;
			rv->GetDesc(&svd);

			if ((svd.uFlags&D3D_SVF_USED) == 0)
			{
				// 사용하지 않은 변수는 패스한다... 
				// 어짜피 사용안하는 값은 설정해 봤자지... 
				// 버퍼가 DYNAMIC 이더라도 별도의 ZERO 버퍼를 갖고 있으니까
				// MapBuffer.data[] 에 다 설정됨
				continue;
			}

			ID3D11ShaderReflectionType* rt = rv->GetType();
			D3D11_SHADER_TYPE_DESC std;
			rt->GetDesc(&std);

			// 변수 만들기
			qbShaderVar& v = vars[cvar++];
			k_strncpy(v.name, svd.Name, 32 - 1);
			v.hash = k_strihash(v.name);
			v.offset = (kint)svd.StartOffset;
			v.size = (kint)svd.Size;
			v.data = _TypeToData(std);
			_AutoMatchFunc(v);

			//-- 실제 HLSL 데이터들은 정렬 패딩 들어가는데, 어떻게 할까?
		}

		// 실제 사용하는 변수를 넣음
		if (cvar > 0)
		{
			pm->vars.resize(cvar);
			for (i = 0; i < cvar; i++)
				pm->vars[i] = vars[i];
		}

		k_delete(vars);

		// 버퍼 맵 저장
		bd->maps[n] = pm;
	}

	return bd;
}

//
bool D11Shader::_BindData(qgShaderType type, kconstpointer data, kint size, kint flags, const char* signature) throw()
{
	D3D_SHADER_MACRO* mcs = _MakeDefinition(_defs);
	InclImpl inc(qgRdh::Instance->GetStub());
	DWORD dw = _MakeFlags(flags);

	//
	bool ret;

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_PS:
			ret = _CreatePixelShader(data, size, mcs, inc, dw, signature);
			break;

		case QGSHADER_VS:
			ret = _CreateVertexShader(data, size, mcs, inc, dw, signature);
			break;

		default:
			ret = false;
			break;
	}

	//
	k_delete(mcs);

	if (!ret)
		return false;
	else
	{
		// 링크 다시해!
		_linked = false;
		return true;
	}
}

//
bool D11Shader::BindFile(qgShaderType type, const char* filename, kint flags) throw()
{
	kint readsize;
	kpointer readdata = qgRdh::Instance->GetStub()->FileRead(filename, &readsize);

	k_return_value_if_fail(readdata, false);

	bool ret = _BindData(type, readdata, readsize, flags, filename);

	k_delete(readdata);

	return ret;
}

//
bool D11Shader::BindData(qgShaderType type, kconstpointer data, kint size, kint flags) throw()
{
	k_return_value_if_fail(data && size > 0, false);

	return _BindData(type, data, size, flags, _key.Data());
}

//
bool D11Shader::BindShader(qgShaderType type, qgShader* shdptr)
{
	k_return_value_if_fail(shdptr, false);

	// 찾는게 있으면 세이더를 참조해둔다
	D11Shader* shd = (D11Shader*)shdptr;
	bool ret;

	switch (K_CAST_ENUM(type))
	{
		case QGSHADER_PS:
			_RemoveData(_ps, _bd_ps, true);
			if (!shd->_ps)
				ret = false;
			else
			{
				_ps = shd->_ps;

				_bd_ps = shd->_bd_ps;
				k_atominc(&_bd_ps->ref);

				ret = true;
			}
			break;

		case QGSHADER_VS:
			_RemoveData(_vs, _bd_vs, true);
			if (!shd->_vs)
				ret = false;
			else
			{
				_vs = shd->_vs;

				_bd_vs = shd->_bd_vs;
				k_atominc(&_bd_vs->ref);

				ret = true;
			}
			break;

		default:
			ret = false;
			break;
	}

	if (!ret)
		return false;
	else
	{
		// 링크 다시해!
		_linked = false;
		return true;
	}
}

//
bool D11Shader::BindShader(qgShaderType type, const char* name) throw()
{
	k_return_value_if_fail(name, false);

	// 찾는다
	D11Shader* shd = (D11Shader*)((D11Rdh*)qgRdh::Instance)->_RoamShaderFind(Key(name));

	return BindShader(type, shd);
}

//
void D11Shader::AddCondition(const char* str)
{
	k_return_if_fail(str);

	char* sz = k_strdupcat(str, ";", NULL);
	char* psz = sz;

	for (;;)
	{
		char* p = k_strchr(psz, ';');
		if (!p)
			break;

		*p = '\0';

		if (*psz)
			_defs.push_back(tpl::string(psz));

		psz = p + 1;
	}

	k_delete(sz);
}

//
void D11Shader::ClearConditions()
{
	_defs.clear();
}

//
bool D11Shader::Link()
{
	k_return_value_if_ok(_linked, true);

	if (_bd_ps == NULL || _bd_vs == NULL)
	{
		// D11은 ps, vs 둘다 있어야 한다
		kobj_tracep(KLV_ERROR, KERR_NOREF);
		return false;
	}

	_linked = true;

	return true;
}

//
void D11Shader::_ProcessMap(ID3D11DeviceContext* ctx) throw()
{
	ID3D11Buffer* bufs[8];
	UINT cnt;

	if (!_bd_ps->maps.empty())
	{
		cnt = _UploadShader(_bd_ps, bufs, ctx);
		if (cnt > 0)
			ctx->PSSetConstantBuffers(0, cnt, bufs);
	}

	if (!_bd_vs->maps.empty())
	{
		cnt = _UploadShader(_bd_vs, bufs, ctx);
		if (cnt > 0)
			ctx->VSSetConstantBuffers(0, cnt, bufs);
	}
}

//
template <ksize_t COUNT>
UINT D11Shader::_UploadShader(BufferData* bd, ID3D11Buffer* (&bufs)[COUNT], ID3D11DeviceContext* ctx) throw()
{
	k_assert_neq(bd->maps.size(), 0);

	//
	UINT cnt = 0;
	D3D11_MAPPED_SUBRESOURCE ms;

	// 맵 데이터 루프
	kForEach(MapBuffer* m, bd->maps)
	{
		if (m->vars.empty() ||
			FAILED(ctx->Map(m->buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms)))
			continue;

		if (_intr != NULL)
		{
			// 세이더 변수 루프
			kForEach(qbShaderVar& v, m->vars)
			{
				v.ptr = (kbyte*)ms.pData + v.offset;

				if (v.role == QGSHDR_AUTO)
					((qbShaderAutoInfo::Function)v.intr)(v.ptr, v);
			}

			// 사용자 지정
			_intr(m->vars.size(), &(*m->vars.begin()), this);
		}
		else
		{
			// 세이더 변수 루프
			kForEach(qbShaderVar& v, m->vars)
			{
				v.ptr = (kbyte*)ms.pData + v.offset;

				if (v.role == QGSHDR_AUTO)
					((qbShaderAutoInfo::Function)v.intr)(v.ptr, v);
			}
		}

		// 해제하고
		ctx->Unmap(m->buf, 0);

		// 성공하면 추가
		bufs[cnt++] = m->buf;

		if (cnt == COUNT)
		{
			// maxbs가 한계다!!!
			return COUNT;
		}
	}

	return cnt;
}

//
qgShaderData D11Shader::_TypeToData(const D3D11_SHADER_TYPE_DESC& std)
{
	if (std.Class == D3D_SVC_STRUCT)
	{
		// 우왕... 우짜지. 구조체 못함. 왜이리 기능이 많아졌음...
		return QGSHDD_UNKNOWN;
	}

	switch (std.Type)
	{
		case D3D_SVT_BOOL: case D3D_SVT_UINT8:
			//-- 테스트 필요
			switch (std.Columns)
			{
				case 1:
					return QGSHDD_BYTE1;

				case 2:
					return QGSHDD_BYTE2;

				case 3:
					return QGSHDD_BYTE3;

				case 4:
					return QGSHDD_BYTE4;
			}
			break;

		case D3D_SVT_INT:
			//-- 테스트 필요
			switch (std.Columns)
			{
				case 1:
					return QGSHDD_INT1;

				case 2:
					return QGSHDD_INT2;

				case 3:
					return QGSHDD_INT3;

				case 4:
					return QGSHDD_INT4;
			}
			break;

		case D3D_SVT_FLOAT:
			//-- 테스트 더 필요
			switch (std.Rows)
			{
				case 1:	// 벡터 = 1x? 행렬
					switch (std.Columns)
					{
						case 1:
							return QGSHDD_FLOAT1;

						case 2:
							return QGSHDD_FLOAT2;

						case 3:
							return QGSHDD_FLOAT3;

						case 4:
							return QGSHDD_FLOAT4;
					}
					break;

				case 2:	// 2x? 행렬
					break;

				case 3:	// 3x? 행렬
					break;

				case 4:	// 4x? 행렬
					switch (std.Columns)
					{
						case 4:
							return QGSHDD_FLOAT16;
					}
					break;
			}
			break;
	}

	return QGSHDD_UNKNOWN;
}

// 자동 변수
namespace D11ShaderWorker
{
	void OrthoProj(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().t_otm;
	}

	void World(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().t_tm[QGTM_WORLD];
	}

	void View(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().t_tm[QGTM_VIEW];
	}

	void Proj(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().t_tm[QGTM_PROJ];
	}

	void ViewProj(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().t_vipr;
	}

	void InvView(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().t_invv;
	}

	void WorldViewProj(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		k_mat4_mul((tpl::Mat4*)data, &qgRdh::Instance->GetPrsn().t_tm[QGTM_WORLD], &qgRdh::Instance->GetPrsn().t_vipr);
	}

	void Tex0(kpointer data, const qbShaderVar& v)
	{
	}

	void Tex1(kpointer data, const qbShaderVar& v)
	{
	}

	void Tex2(kpointer data, const qbShaderVar& v)
	{
	}

	void Tex3(kpointer data, const qbShaderVar& v)
	{
	}

	void Tex4(kpointer data, const qbShaderVar& v)
	{
	}

	void Tex5(kpointer data, const qbShaderVar& v)
	{
	}

	void Tex6(kpointer data, const qbShaderVar& v)
	{
	}

	void Tex7(kpointer data, const qbShaderVar& v)
	{
	}

	void PropVec0(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = qgRdh::Instance->GetPrsn().ps_v[0];
	}

	void PropVec1(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = qgRdh::Instance->GetPrsn().ps_v[1];
	}

	void PropVec2(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = qgRdh::Instance->GetPrsn().ps_v[2];
	}

	void PropVec3(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Vec4));
		*(tpl::Vec4*)data = qgRdh::Instance->GetPrsn().ps_v[3];
	}

	void PropMat0(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().ps_m[0];
	}

	void PropMat1(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().ps_m[1];
	}

	void PropMat2(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().ps_m[2];
	}

	void PropMat3(kpointer data, const qbShaderVar& v)
	{
		k_assert_eq(v.size, sizeof(tpl::Mat4));
		*(tpl::Mat4*)data = qgRdh::Instance->GetPrsn().ps_m[3];
	}

	void MatPalette(kpointer data, const qbShaderVar& v)
	{
		tpl::Mat4* m = (tpl::Mat4*)data;
		auto& prsn = qgRdh::Instance->GetPrsn();
		memcpy(m, prsn.pw_pcb, sizeof(tpl::Mat4) * prsn.pw_cnt);
	}

#define SV(m,t)	{0, "s" K_STRING(m), t, (qbShaderAutoInfo::Function)m}
	qbShaderAutoInfo Autos[QGSHDA_MAX_VALUE] =
	{
		SV(OrthoProj, QGSHDA_ORTHO_PROJ),
		SV(World, QGSHDA_WORLD),
		SV(View, QGSHDA_VIEW),
		SV(Proj, QGSHDA_PROJ),
		SV(ViewProj, QGSHDA_VIEW_PROJ),
		SV(InvView, QGSHDA_INV_VIEW),
		SV(WorldViewProj, QGSHDA_WORLD_VIEW_PROJ),
		SV(Tex0, QGSHDA_TEX0),
		SV(Tex1, QGSHDA_TEX1),
		SV(Tex2, QGSHDA_TEX2),
		SV(Tex3, QGSHDA_TEX3),
		SV(Tex4, QGSHDA_TEX4),
		SV(Tex5, QGSHDA_TEX5),
		SV(Tex6, QGSHDA_TEX6),
		SV(Tex7, QGSHDA_TEX7),
		SV(PropVec0, QGSHDA_PROP_VEC0),
		SV(PropVec1, QGSHDA_PROP_VEC1),
		SV(PropVec2, QGSHDA_PROP_VEC2),
		SV(PropVec3, QGSHDA_PROP_VEC3),
		SV(PropMat0, QGSHDA_PROP_MAT0),
		SV(PropMat1, QGSHDA_PROP_MAT1),
		SV(PropMat2, QGSHDA_PROP_MAT2),
		SV(PropMat3, QGSHDA_PROP_MAT3),
		SV(MatPalette, QGSHDA_MAT_PALETTE),
	};
#undef SV
}

//
void D11Shader::_AutoInitFunc()
{
	for (ksize_t i = 0; i < K_COUNTOF(D11ShaderWorker::Autos); i++)
		D11ShaderWorker::Autos[i].hash = k_strihash(D11ShaderWorker::Autos[i].name);
}

//
kpointer D11Shader::_AutoMatchFunc(qbShaderVar& v)
{
	for (ksize_t i = 0; i < K_COUNTOF(D11ShaderWorker::Autos); i++)
	{
		if (v.hash == D11ShaderWorker::Autos[i].hash &&
			k_strieqv(v.name, D11ShaderWorker::Autos[i].name))
		{
			// 찾음
			v.role = QGSHDR_AUTO;
			v.intr = D11ShaderWorker::Autos[i].func;

			return NULL;
		}
	}

	// 없음
	v.role = QGSHDR_MANUAL;
	v.intr = NULL;

	return NULL;
}

//
kconstpointer D11Shader::BaseKey(kNodeBase* ptr)
{
	D11Shader* self = (D11Shader*)ptr;
	return &self->_key;
}

//
bool D11Shader::BaseEq(kNodeBase* ptr, const Key& k2)
{
	D11Shader* self = (D11Shader*)ptr;
	const Key& k1 = self->_key;
	return k1 == k2;
}

//
ksize_t D11Shader::KeyHash(const Key& key)
{
	return key.GetHashCode();
}

#endif	// _QG_USE_D11
